﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Xml;

using Microsoft.Health;
using Microsoft.Health.Events;
using Microsoft.Health.Web;

public partial class SendTestNotification : HealthServicePage
{
    SubscriptionManager _manager;
    Subscription _subscription;

    protected void Page_Load(object sender, EventArgs e)
    {
        title.Text = Request.QueryString["Operation"];

        string subscriptionIdString = Request.QueryString["Id"];

        if (subscriptionIdString == null)
        {
            return;
        }
        Guid subscriptionId = new Guid(subscriptionIdString);

        c_subscriptionIdText.InnerText = subscriptionId.ToString();

        _manager = new SubscriptionManager(ApplicationConnection);
        _subscription = SubscriptionManagerExtension.GetSubscription(_manager, subscriptionId);

        c_notification.InnerText = _subscription.CommonSubscriptionData.NotificationChannel.ToString();

        c_notificationKey.InnerText = Convert.ToBase64String(_subscription.CommonSubscriptionData.SharedKeyInfo.Key);
        c_notificationKeyVersion.InnerText = _subscription.CommonSubscriptionData.SharedKeyInfo.Version;

        SetDefaultRadioButtonIfNoneSelected(c_radioButtonNotificationData);
        SetDefaultRadioButtonIfNoneSelected(c_radioButtonNotificationUrl);
        SetDefaultRadioButtonIfNoneSelected(c_radioButtonAuthenticationData);

        c_linkUp.HRef = "update.aspx?Id=" + subscriptionIdString;

        Render(null, null);

        if (!Page.IsPostBack)
        {
            LoadCurrentSettings();
        }
    }

    void SetDefaultRadioButtonIfNoneSelected(RadioButtonList list)
    {
        if (list.SelectedIndex == -1)
        {
            list.SelectedIndex = 0;
        }
    }

    protected void Render(object sender, EventArgs e)
    {
        title.Text = Request.QueryString["Operation"];

        string subscriptionIdString = Request.QueryString["Id"];

        if (subscriptionIdString == null)
        {
            return;
        }
        Guid subscriptionId = new Guid(subscriptionIdString);

        c_subscriptionIdText.InnerText = subscriptionId.ToString();

        c_subscriptionType.InnerText = _subscription.GetType().Name;

        HealthRecordItemChangedSubscription healthRecordItemChangedSubscription =
            _subscription as HealthRecordItemChangedSubscription;

        if (healthRecordItemChangedSubscription != null)
        {
            foreach (Guid typeId in healthRecordItemChangedSubscription.Filters[0].TypeIds)
            {
                HtmlTableRow row = new HtmlTableRow();
                c_tableTypes.Rows.Add(row);

                HtmlTableCell cell = new HtmlTableCell();

                CheckBox checkbox = new CheckBox();
                checkbox.ID = typeId.ToString();
                cell.Controls.Add(checkbox);
                row.Cells.Add(cell);

                Type type = ItemTypeManager.GetRegisteredTypeForTypeId(typeId);
                cell = new HtmlTableCell();
                cell.InnerText = type.Name;
                row.Cells.Add(cell);
            }
        }
    }

    List<Guid> GetCheckedTypes()
    {
        HealthRecordItemChangedSubscription healthRecordItemChangedSubscription =
            _subscription as HealthRecordItemChangedSubscription;

        List<Guid> checkedTypes = new List<Guid>();

        foreach (HtmlTableRow row in c_tableTypes.Rows)
        {
            HtmlTableCell cell = row.Cells[0];
            CheckBox checkbBox = cell.Controls[0] as CheckBox;

            if (checkbBox.Checked)
            {
                Guid typeId = new Guid(checkbBox.ID);
                checkedTypes.Add(typeId);
            }
        }

        return checkedTypes;
    }

    string GenerateNotificationXml()
    {
        string xml = @"
        <wc:notifications xmlns:wc='urn:com.microsoft.wc.notification'>
            <notification>
                <common>
                    <subscription-id>{0}</subscription-id>
                </common>
                <record-change-notification>
                    <person-id>{1}</person-id>
                    <record-id>{2}</record-id>
                    <things>
                            {3}
                    </things>
                </record-change-notification>
            </notification>
        </wc:notifications>
        ";

        StringBuilder thingIdString = new StringBuilder();

        int index = c_radioButtonNotificationData.SelectedIndex;
        Guid personId = Guid.Empty;
        Guid recordId = Guid.Empty;
        
        if (index == 0)     // fake instance ids...
        {
            for (int i = 0; i < 5; i++)
            {
                thingIdString.Append("<thing><thing-id>" + Guid.NewGuid().ToString() + "</thing-id></thing>");
            }
        }
        else if (index == 1)  // empty instance list...
        {
            // nothing to do here...
        }
        else if (index == 2)  // grab real instances from the record...
        {
            HealthRecordItemChangedSubscription healthRecordItemChangedSubscription =
                    _subscription as HealthRecordItemChangedSubscription;

            foreach (Guid typeId in GetCheckedTypes())
            {
                foreach (Guid instanceId in GetInstanceIdsForType(typeId))
                {
                    thingIdString.Append("<thing><thing-id>" + instanceId.ToString() + "</thing-id></thing>");
                }
            }

            personId = PersonInfo.PersonId;
            recordId = PersonInfo.SelectedRecord.Id;
        }

        string finalXml = String.Format(xml,
                                        _subscription.CommonSubscriptionData.Id,
                                        personId,
                                        recordId,
                                        thingIdString);

        return finalXml;
    }

    string GenerateNotificationAuthHeader(string notificationXml)
    {
        // hash to include in the the notification.
        string keyVersion = _subscription.CommonSubscriptionData.SharedKeyInfo.Version;

        int selectedIndex = c_radioButtonAuthenticationData.SelectedIndex;

        if (selectedIndex == 1)     // bad hmac value
        {
            notificationXml += "BadStuff";      // will generate wrong hmac...
        }
        else if (selectedIndex == 2) // bad version key
        {
            keyVersion = "IncorrectKeyVersion";
        }

        byte[] notificationBytes = Encoding.UTF8.GetBytes(notificationXml);
        string hash = null;
        using (HMACSHA256 alg = new HMACSHA256(_subscription.CommonSubscriptionData.SharedKeyInfo.Key))
        {
            hash = Convert.ToBase64String(alg.ComputeHash(notificationBytes));
        }

        string authHeader = String.Format(
            "HVEventingSharedKey {0}:{1}:{2}",
            _subscription.CommonSubscriptionData.Id,
            keyVersion,
            hash);

        return authHeader;
    }

    void VerifyApplicationAccessToType(Guid typeId)
    {
        List<Guid> items = new List<Guid>();
        items.Add(typeId);
        Collection<HealthRecordItemTypePermission> permissions =
                PersonInfo.SelectedRecord.QueryPermissions(items);

        if (permissions.Count == 0)
        {
            Type type = ItemTypeManager.GetRegisteredTypeForTypeId(typeId);
            throw new InvalidOperationException("You application is not authorized to access the " + type.Name + " type.");
        }
    }

    List<Guid> GetInstanceIdsForType(Guid typeId)
    {
        VerifyApplicationAccessToType(typeId);

        List<Guid> instanceIds = new List<Guid>();

        HealthRecordSearcher searcher = PersonInfo.SelectedRecord.CreateSearcher(typeId);
        searcher.Filters[0].MaxItemsReturned = 5;

        HealthRecordItemCollection items = searcher.GetMatchingItems()[0];

        foreach (HealthRecordItem item in items)
        {
            instanceIds.Add(item.Key.Id);
        }

        return instanceIds;
    }

    protected void linkDeleteClick(Object sender, CommandEventArgs e)
    {
        Guid id = new Guid((string)e.CommandArgument);

        HealthRecordItemChangedSubscription healthRecordItemChangedSubscription =
            _subscription as HealthRecordItemChangedSubscription;

        IList<Guid> typeIds = healthRecordItemChangedSubscription.Filters[0].TypeIds;

        if (healthRecordItemChangedSubscription != null)
        {
            for (int i = typeIds.Count - 1; i >= 0; i--)
            {
                if (typeIds[i] == id)
                {
                    typeIds.RemoveAt(i);
                }
            }
        }
    }

    string GetNotificationUrl()
    {
        int selectedIndex = c_radioButtonNotificationUrl.SelectedIndex;


        if (selectedIndex == 0)
        {
            HttpNotificationChannel channel = _subscription.CommonSubscriptionData.NotificationChannel as HttpNotificationChannel;

            if (channel != null)
            {
                return channel.Url.OriginalString;
            }
        }
        else if (selectedIndex == 1)
        {
            int spot = Request.Url.AbsoluteUri.IndexOf("/SendTestNotification.aspx");

            string url = Request.Url.AbsoluteUri.Substring(0, spot) + "/NotificationHandlerPage.ashx";

            return url;
        }
        else if (selectedIndex == 2)
        {
            return c_textSpecifiedUrl.Text;
        }

        return null;
    }

    private string SettingsFilename
    {
        get { return MapPath(@"temp\tmpTestSettings.txt"); }
    }

    private void SaveCurrentSettings()
    {
        using (StreamWriter writer = File.CreateText(SettingsFilename))
        {
            writer.WriteLine(c_radioButtonNotificationUrl.SelectedIndex);
            writer.WriteLine(c_textSpecifiedUrl.Text);
            writer.WriteLine(c_radioButtonNotificationData.SelectedIndex);

            List<Guid> checkedTypes = GetCheckedTypes();
            writer.WriteLine(checkedTypes.Count);
            foreach (Guid typeId in checkedTypes)
            {
                writer.WriteLine(typeId);
            }

            writer.WriteLine(c_radioButtonAuthenticationData.SelectedIndex);
        }
    }

    private void LoadCurrentSettings()
    {
        if (!File.Exists(SettingsFilename))
        {
            return;
        }

        using (StreamReader reader = File.OpenText(SettingsFilename))
        {
            c_radioButtonNotificationUrl.SelectedIndex = Int32.Parse(reader.ReadLine());
            c_textSpecifiedUrl.Text = reader.ReadLine();
            c_radioButtonNotificationData.SelectedIndex = Int32.Parse(reader.ReadLine());

            int checkedTypeCount = Int32.Parse(reader.ReadLine());
            List<Guid> typeIds = new List<Guid>();
            for (int i = 0; i < checkedTypeCount; i++)
            {
                Guid typeId = new Guid(reader.ReadLine());
                typeIds.Add(typeId);
            }

            c_radioButtonAuthenticationData.SelectedIndex = Int32.Parse(reader.ReadLine());

            foreach (HtmlTableRow row in c_tableTypes.Rows)
            {
                HtmlTableCell cell = row.Cells[0];
                CheckBox checkBox = cell.Controls[0] as CheckBox;

                Guid typeId = new Guid(checkBox.ID);
                if (typeIds.Contains(typeId))
                {
                    checkBox.Checked = true;
                }
            }
        }
    }


    protected void testClick(Object sender, EventArgs e)
    {
        SaveCurrentSettings();

        c_divTestMessage.Visible = false;

        string finalXml = GenerateNotificationXml();
        string authHeaderValue = GenerateNotificationAuthHeader(finalXml);

        string result = String.Empty;

        HttpNotificationChannel channel = _subscription.CommonSubscriptionData.NotificationChannel as HttpNotificationChannel;
        if (channel != null)
        {
            string url = GetNotificationUrl();

            WebRequest request = HttpWebRequest.Create(url);
            request.Method = "POST";
            request.ContentLength = finalXml.Length;
            request.ContentType = "xml";
            request.Headers[HttpRequestHeader.Authorization] = authHeaderValue;

            try
            {
                using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
                {
                    writer.Write(finalXml);
                }

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                result = "<b>Status:</b> " + response.StatusDescription + "<br /><b>Text returned from method:</b> <br />";

                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    result += Server.HtmlEncode(reader.ReadToEnd());
                    reader.Close();
                }
            }
            catch (Exception ex)
            {
                result += ex.Message;
            }
        }

        c_textResult.Text = result;
    }

    protected void viewTestMessageClick(object sender, EventArgs e)
    {
        SaveCurrentSettings();

        string finalXml = GenerateNotificationXml();
        string authHeaderValue = GenerateNotificationAuthHeader(finalXml);

        XmlDocument doc = new XmlDocument();
        doc.LoadXml(finalXml);

        StringBuilder stringBuilder = new StringBuilder();

        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Indent = true;
        settings.Encoding = Encoding.Unicode;

        using (XmlWriter writer = XmlTextWriter.Create(stringBuilder, settings))
        {
            doc.WriteTo(writer);
        }

        c_divTestMessage.Visible = true;

        c_divTestMessage.InnerHtml = "<p>Header: <pre>" + authHeaderValue + "</pre></p><pre>" + HttpUtility.HtmlEncode(stringBuilder.ToString()) + "</pre>";
    }
}